/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/
/* 
Promise中的执行器方法会立即执行
值一旦确定就不允许再次改变
then方法：
  链式调用 （异步逻辑处理）
  可以多次调用
  返回一个Promise对象 但不允许返回自身
  方法参数为可选 如果不传入回调函数 则会直接返回上一个then的返回
静态方法 all, resolve
*/
const PENDING = 'pengding'
const FULLFILLED = 'fullfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 构造方法接受一个执行器方法
  constructor (executor) {
    try {
      // Promise中的代码会立即执行
      // 需要立即调用执行器方法
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  // Promsie状态
  status = PENDING
  // 成功的值
  value = undefined
  // 失败原因
  reason = undefined
  // 成功回调
  sucessCallback = []
  // 失败回调
  failCallback = []

  resolve = value => {
    // 如果状态已经改变，则直接返回
    if (this.status !== PENDING) return

    this.status = FULLFILLED
    
    this.value = value

    // 异步任务执行完调用回调函数
    while (this.sucessCallback.length) {
      this.sucessCallback.shift()()
    }
  }
  
  reject = reason => {
    // 如果状态已经改变，则直接返回
    if (this.status !== PENDING) return
    
    this.status = REJECTED
    
    this.reason = reason

    // 异步任务执行完调用回调函数
    while (this.failCallback.length) {
      this.failCallback.shift()()
    }
  }

  then = (sucessCallback, failCallback) => {
    // 返回一个Promise对象，实现链式调用
    const promise = new MyPromise((resolve, reject) => {
      // 兼容then参数为空的情况
      if (!sucessCallback) {
        sucessCallback = value => value
      }
      if (!failCallback) {
        failCallback = reason => { throw reason }
      }
      
      // 成功
      if (this.status === FULLFILLED) {
        try {
          // 需要获取到当前 promise 对象，并将回调结果传递给该promisse对象
          setTimeout(() => {
            try {
              let r = sucessCallback(this.value)
              resolvePromise(promise, r, resolve, reject)
            } catch (error) {
              reject(error)
            }
          }, 0)
        } catch (error) {
          this.reject(error)
        }
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            // 失败
            let r = failCallback(this.reason)
            resolvePromise(promise, r, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        }, 0)
      } else {
        // PENDING 说明为异步任务
        setTimeout(() => {
          try {
            this.sucessCallback.push(() => {
              let r = sucessCallback(this.value)
              resolvePromise(promise, r, resolve, reject)
            })
            this.failCallback.push(() => {
              let r = failCallback(this.reason)
              resolvePromise(promise, r, resolve, reject)
            })
          } catch (error) {
            reject(error)
          }
        }, 0)
      }
    })

    return promise
  }
  
  // 无论promsie最终状态成功与否 其回调函数 都会被执行一次
  // 可以在 finally 后使用链式调用获取到当前 Promise对象的执行结果
  // 如果回调函数中返回了 Promise对象，则需要等待其完成才能进行后续链式调用
  finally (callback) {
    return this.then(value => {
      return MyPromise.resolve(callback()).then(() => value)
    }, reason => {
      return MyPromise.resolve(callback()).then(() => { throw reason })
    })
  }

  catch (failCallback) {
    return this.then(undefined, failCallback)
  }

  // 转换为Promise对象
  static resolve (value) {
    // 如果传入的为Promise对象直接返回
    if (value instanceof MyPromise) {
      return value
    }
    // 普通值 转换为Promise对象
    return new MyPromise(resolve => resolve(value))
  }

  static all (array) {
    const result = []
    // 数据标记
    let index = 0

    return new MyPromise((resolve, reject) => {
      function addToResult (key, value) {
        result[key] = value
        index++
        // 判断 数组中的 promise 是否全部执行完
        if (index === array.length) {
          resolve(result)
        }
      }
      
      for (let i = 0; i < array.length; i++) {
        let current = array[i]
        if (current instanceof MyPromise) {
          // 如果当前元素为 promsie
          // 执行promise将其值 添加到result中
          current.then(value => addToResult(i, value), reject)
        } else {
          // 如果为普通数据则直接加入到数组中
          addToResult(i, current)
        }
      }
    })
  }
}

function resolvePromise (promise, r, resolve, reject) {
  // 不允许返回自身
  if (r === promise) {
    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
  }

  // 如果当前回调的返回值是Promise对象，则需要等待执行完成再将结果往下一个then传递
  if (r instanceof MyPromise) {
    r.then(resolve, reject)
  } else {
    resolve(r)
  }
}

module.exports = MyPromise